From: Marco Trevisan (TreviƱo) Date: Wed, 25 Sep 2019 18:56:46 +0000 (+0200) Subject: ibusManager, inputMethod: Cancel async ibus calls chain on disconnect X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~811^2^2~14 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=1cc766d6369361a3c8b80acfb6236c7ef8e0bdaf;p=gnome-shell.git ibusManager, inputMethod: Cancel async ibus calls chain on disconnect The shell tries to spawn the ibus daemon on startup if unavailable, however as per commit 8adfc5b1 we also force restarting it once the X11 server is available. Unfortunately this could cause a race if we disconnect while we were already connected to an ibus daemon, but still in the process of going through the various nested calls. In fact the ::disconnect callback didn't stop any further async ibus call that, even if failing, would have eventually triggered the emission of a 'ready' signal and to the Keyboard's callback, leading under X11 to a full grab owned by ibus daemon. In order to avoid this and keep control of the calls order, use in both IbusManager and InputMethod a cancellable that is setup before connecting to the bus, and that is cancelled on disconnection. Then handle the finish() calls properly, using try/catch to validate the returned value, taking in account the potential error and just not proceeding in case of cancellation. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712 --- diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js index 2011321ee..55e2fd97b 100644 --- a/js/misc/ibusManager.js +++ b/js/misc/ibusManager.js @@ -72,6 +72,11 @@ var IBusManager = class { } _clear() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + if (this._panelService) this._panelService.destroy(); @@ -86,28 +91,41 @@ var IBusManager = class { } _onConnected() { - this._ibus.list_engines_async(-1, null, this._initEngines.bind(this)); + this._cancellable = new Gio.Cancellable(); + this._ibus.list_engines_async(-1, this._cancellable, + this._initEngines.bind(this)); this._ibus.request_name_async(IBus.SERVICE_PANEL, - IBus.BusNameFlag.REPLACE_EXISTING, - -1, null, - this._initPanelService.bind(this)); + IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable, + this._initPanelService.bind(this)); } _initEngines(ibus, result) { - let enginesList = this._ibus.list_engines_async_finish(result); - if (enginesList) { + try { + let enginesList = this._ibus.list_engines_async_finish(result); for (let i = 0; i < enginesList.length; ++i) { let name = enginesList[i].get_name(); this._engines.set(name, enginesList[i]); } this._updateReadiness(); - } else { + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + return; + + logError(e); this._clear(); } } _initPanelService(ibus, result) { - let success = this._ibus.request_name_async_finish(result); + let success = false; + try { + success = !!this._ibus.request_name_async_finish(result); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + return; + logError(e); + } + if (success) { this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(), object_path: IBus.PATH_PANEL }); @@ -134,7 +152,7 @@ var IBusManager = class { } catch (e) { } // If an engine is already active we need to get its properties - this._ibus.get_global_engine_async(-1, null, (i, result) => { + this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, result) => { let engine; try { engine = this._ibus.get_global_engine_async_finish(result); @@ -206,8 +224,18 @@ var IBusManager = class { return; } - this._ibus.set_global_engine_async(id, this._MAX_INPUT_SOURCE_ACTIVATION_TIME, - null, callback || null); + this._ibus.set_global_engine_async(id, + this._MAX_INPUT_SOURCE_ACTIVATION_TIME, + this._cancellable, (_bus, res) => { + try { + this._ibus.set_global_engine_async_finish(res); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + logError(e); + } + if (callback) + callback(); + }); } preloadEngines(ids) { @@ -227,7 +255,7 @@ var IBusManager = class { this._ibus.preload_engines_async( ids, -1, - null, + this._cancellable, null); this._preloadEnginesId = 0; return GLib.SOURCE_REMOVE; diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js index 1ca70c3df..42bf7f632 100644 --- a/js/misc/inputMethod.js +++ b/js/misc/inputMethod.js @@ -1,6 +1,6 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported InputMethod */ -const { Clutter, GLib, GObject, IBus } = imports.gi; +const { Clutter, GLib, Gio, GObject, IBus } = imports.gi; const Keyboard = imports.ui.status.keyboard; @@ -47,12 +47,22 @@ class InputMethod extends Clutter.InputMethod { } _onConnected() { - this._ibus.create_input_context_async ('gnome-shell', -1, null, - this._setContext.bind(this)); + this._cancellable = new Gio.Cancellable(); + this._ibus.create_input_context_async ('gnome-shell', -1, + this._cancellable, this._setContext.bind(this)); } _setContext(bus, res) { - this._context = this._ibus.create_input_context_async_finish(res); + try { + this._context = this._ibus.create_input_context_async_finish(res); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { + logError(e); + this._clear(); + } + return; + } + this._context.connect('commit-text', this._onCommitText.bind(this)); this._context.connect('delete-surrounding-text', this._onDeleteSurroundingText.bind(this)); this._context.connect('update-preedit-text', this._onUpdatePreeditText.bind(this)); @@ -64,6 +74,11 @@ class InputMethod extends Clutter.InputMethod { } _clear() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + this._context = null; this._hints = 0; this._purpose = 0; @@ -244,17 +259,19 @@ class InputMethod extends Clutter.InputMethod { if (event.type() == Clutter.EventType.KEY_RELEASE) state |= IBus.ModifierType.RELEASE_MASK; - this._context.process_key_event_async(event.get_key_symbol(), - event.get_key_code() - 8, // Convert XKB keycodes to evcodes - state, -1, null, - (context, res) => { - try { - let retval = context.process_key_event_async_finish(res); - this.notify_key_event(event, retval); - } catch (e) { - log(`Error processing key on IM: ${e.message}`); - } - }); + this._context.process_key_event_async( + event.get_key_symbol(), + event.get_key_code() - 8, // Convert XKB keycodes to evcodes + state, -1, this._cancellable, + (context, res) => { + try { + let retval = context.process_key_event_async_finish(res); + this.notify_key_event(event, retval); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log(`Error processing key on IM: ${e.message}`); + } + }); return true; } });